package org.example.business_logic;

import javafx.util.Pair;
import org.example.business_logic.action.*;
import org.example.business_logic.component.Baffle;
import org.example.physics_engine.AbstractPythicsWorld;
import org.example.physics_engine.collision.contact.ContactConstraint;
import org.example.physics_engine.collision.manifold.Manifold;
import org.example.physics_engine.body.PhysicsBody;
import org.example.physics_engine.geometry.Vector2d;
import org.example.facade.component.Component_Type;
import org.example.facade.panel.GizmoPhysicsBody;

import java.util.ArrayList;
import java.util.HashMap;
import java.util.List;
import java.util.Map;

public class GameWorld extends AbstractPythicsWorld<GizmoPhysicsBody> {
    private final List<UpdateListener> tickListeners;

    protected final Map<Component_Type, List<PhysicsBody>> bodyTypeMap;

    public GameWorld(Vector2d gravity) {
        super(gravity);

        // k是某个组件类型Component_Type，对应的value是一个列表，存着所有该类型的加到游戏画布上的组件
        // 如Component_Type.TRIANGLE，对应1个含有两个TRIANGLE的list
        bodyTypeMap = new HashMap<>();

        List<PhysicsBody> balls = new ArrayList<>();
        bodyTypeMap.put(Component_Type.BALL, balls);
        tickListeners = new ArrayList<>();

        tickListeners.add(new BallAction(balls));
        tickListeners.add(new HoleAction(balls, bodyTypeMap.computeIfAbsent(Component_Type.HOLE, k -> new ArrayList<>()), bodies));
        tickListeners.add(new OrbitAction(balls, bodyTypeMap.computeIfAbsent(Component_Type.ORBIT, k -> new ArrayList<>()), gravity));
        tickListeners.add(new OrbitAction(balls, bodyTypeMap.computeIfAbsent(Component_Type.CURVED_ORBIT, k -> new ArrayList<>()), gravity));
        tickListeners.add(new BarrierAction(balls, bodyTypeMap.computeIfAbsent(Component_Type.BOUNDARY, k -> new ArrayList<>())));
        tickListeners.add(new BarrierAction(balls, bodyTypeMap.computeIfAbsent(Component_Type.CIRCLE, k -> new ArrayList<>())));
        tickListeners.add(new BarrierAction(balls, bodyTypeMap.computeIfAbsent(Component_Type.TRIANGLE, k -> new ArrayList<>())));
        tickListeners.add(new BarrierAction(balls, bodyTypeMap.computeIfAbsent(Component_Type.RECTANGLE, k -> new ArrayList<>())));
        tickListeners.add(new BaffleAction(balls, bodyTypeMap.computeIfAbsent(Component_Type.LEFT_BARRIER, k -> new ArrayList<>())));
        tickListeners.add(new BaffleAction(balls, bodyTypeMap.computeIfAbsent(Component_Type.RIGHT_BARRIER, k -> new ArrayList<>())));
    }

    @Override
    public void addBody(GizmoPhysicsBody body) {
        super.addBody(body);

        Component_Type gizmoType = body.getComponentType();
        bodyTypeMap.computeIfAbsent(gizmoType, k -> new ArrayList<>()).add(body);
    }

    @Override
    public void removeBody(GizmoPhysicsBody body) {
        super.removeBody(body);

        Component_Type type = body.getComponentType();
        List<PhysicsBody> list = bodyTypeMap.get(type);
        if (list != null) {
            list.remove(body);
        }
    }

    @Override
    public void removeAllBodies() {
        super.removeAllBodies();

        // 从类型Map中移除
        for (Map.Entry<Component_Type, List<PhysicsBody>> entry : bodyTypeMap.entrySet()) {
            entry.getValue().clear();
        }
    }

    @Override
    public void updateStep() {
        List<Pair<Manifold, Pair<PhysicsBody, PhysicsBody>>> pairs = new ArrayList<>();
        // 碰撞检测，返回碰撞检测
        for (UpdateListener listener : tickListeners) {
            List<Pair<Manifold, Pair<PhysicsBody, PhysicsBody>>> pair = listener.tick();
            pairs.addAll(pair);
        }
        List<ContactConstraint> contactConstraints = collisionDetector.preLocalSolve(pairs);
        collisionDetector.LocalSolve(resolver, gravity, contactConstraints, bodyTypeMap.get(Component_Type.BALL));
    }

    public void flipperUp(Baffle.Direction direction) {
        if (direction == Baffle.Direction.LEFT) {
            for (PhysicsBody physicsBody : bodyTypeMap.get(Component_Type.LEFT_BARRIER)) {
                Baffle baffle = (Baffle) physicsBody.getShape();
                baffle.rise();
            }
        } else if (direction == Baffle.Direction.RIGHT) {
            for (PhysicsBody physicsBody : bodyTypeMap.get(Component_Type.RIGHT_BARRIER)) {
                Baffle baffle = (Baffle) physicsBody.getShape();
                baffle.rise();
            }
        }
    }

    public void flipperDown(Baffle.Direction direction) {
        if (direction == Baffle.Direction.LEFT) {
            for (PhysicsBody physicsBody : bodyTypeMap.get(Component_Type.LEFT_BARRIER)) {
                Baffle baffle = (Baffle) physicsBody.getShape();
                baffle.down();
            }
        } else if (direction == Baffle.Direction.RIGHT) {
            for (PhysicsBody physicsBody : bodyTypeMap.get(Component_Type.RIGHT_BARRIER)) {
                Baffle baffle = (Baffle) physicsBody.getShape();
                baffle.down();
            }
        }
    }

}
